#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/init.h>
#include <linux/fs.h>		     /* inode定义 */
#include <linux/uaccess.h>	     /* copy_**_user */
#include <linux/cdev.h>		     /* cdev各函数 */
#include <linux/byteorder/generic.h> /* 大小端字节序 */
#include <linux/semaphore.h>	     /* semaphore */
#include <linux/string.h>

#include "e581.h"

#define _printk printk

MODULE_LICENSE("LGPL");
MODULE_AUTHOR("Anod Chow");

static gps_driver driver = 
  {
    .is_open = false,
    .usable_time = 0,
  };


//  ********************************************
//   以下是usb设备部分
//  ********************************************

static int get_line_coding( line_coding *_lcoding )
{
  // 实际上就是通过控制端点0进行数据的发送，然后接收端点返回的数据

  int translate_len;

  down_interruptible( &driver.messaging );
  translate_len = usb_control_msg( driver.gps_usb_dev, /* usb_device */
				   usb_rcvctrlpipe( driver.gps_usb_dev, ep_0), /* 端点0 */
				   RC_GET_LCODING,
				   IN_REQUEST, /* bmRequestType */
				   0, /* 看24页 */
				   0, /* 端点0，还是看24页 */
				   _lcoding, /* 因为不发送独立数据，此项为空 */
				   sizeof( _lcoding ), /* 缓冲区长度 */
				   OUT_OF_TIME );      /* 超时 */
  up( &driver.messaging );
  if( translate_len < 0 ) // 失败
    {
      return translate_len;
    }
  else
    {
      _lcoding->dwDTERate = cpu_to_le32( _lcoding->dwDTERate ); /* 字节序转换 */

      return 0;
    }
}

static int set_line_coding( line_coding *_lcoding )
{
  _lcoding->dwDTERate = cpu_to_le32( _lcoding->dwDTERate );

  int translate_len;

  // 详细协议看25页
  down_interruptible( &driver.messaging );
  translate_len = usb_control_msg( driver.gps_usb_dev,
				   usb_sndctrlpipe(driver.gps_usb_dev, ep_0),
				   RC_SET_LCODING,
				   OUT_REQUEST,
				   0,
				   0,
				   _lcoding,
				   sizeof( _lcoding ),
				   OUT_OF_TIME );
  up( &driver.messaging );
  if( translate_len < 0 )// 失败
    {
      return translate_len;
    }
  else
    {
      return 0;
    }
}

static int set_control_line_state( uint16_t state )
{
  // 实际上只有state == 1/2/3时才是有效的，不过这里也不做什么检查了，整个文件都是自己写的没有必要
  int translate_len;

  // 详细情况看25页
  down_interruptible( &driver.messaging );
  translate_len = usb_control_msg( driver.gps_usb_dev,
				   usb_sndctrlpipe(driver.gps_usb_dev, ep_0),
				   RC_SET_CTRLLST,
				   OUT_REQUEST,
				   state,
				   0,
				   NULL,
				   0,
				   OUT_OF_TIME);
  up( &driver.messaging );
  if( translate_len < 0 )
    {
      return translate_len;
    }
  else
    {
      return 0;
    }
}

static int e581_plugin( struct usb_interface *_intf, const struct usb_device_id *_id )
{
  // 主要是做几件事情
  // 1-设置driver
  // 2-与e581通信，然后set line coding
  struct usb_host_interface *iface_desc;
  int    i;
  struct usb_endpoint_descriptor *endpoint;

  //1.1 driver.gps_usb_dev
  driver.gps_usb_dev = interface_to_usbdev( _intf );

  //1.2 driver.read_ep
  iface_desc = _intf->cur_altsetting;
  for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
    endpoint = &iface_desc->endpoint[i].desc;
    
    if ( usb_endpoint_is_bulk_in(endpoint) ) {
      driver.read_ep = endpoint->bEndpointAddress;
      break;
    }
  }
  if ( driver.read_ep == 0 ){
    printk( KERN_ALERT "no bulk in endpoint found!\n" );
    return -1;
  }

  //1-3 driver.usable_time
  driver.usable_time = jiffies + HZ*60;

  //2
  line_coding *coding = kmalloc( sizeof(line_coding), GFP_KERNEL );
  get_line_coding( coding );
  kfree (coding);
  set_control_line_state( 0 );

  return 0;
}

static void e581_plugout( struct usb_interface *_intf )
{
  driver.gps_usb_dev = NULL;
  driver.read_ep = 0;
  driver.usable_time = 0;
  driver.is_open = false;
  init_MUTEX( &driver.open_mutex );

  down_interruptible( &driver.messaging );
  up( &driver.messaging );
}

static struct usb_device_id e581_id [] = {
  { USB_DEVICE_AND_INTERFACE_INFO( E581_VENDOR,E581_PRODUCT,  
				   INTF_CLASS,INTF_SUBCLASS,INTF_PROTOCOL) },
  { }					/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, e581_id);

static struct usb_driver e581_driver = 
  {
    .name = "e581 driver",
    .id_table = e581_id,
    .probe = e581_plugin,
    .disconnect = e581_plugout,
  };


//  ********************************************
//   以下是字符设备部分
//  ********************************************

static int chrfile_open( struct inode *_inode, struct file *_file )
{
  // 详细协议过程需要看一看从BusHound抓取的包

  bool refuse = false;
  int ret_code;

  // 首先：看看是否接入设备后到1分钟以上，否则设备不会响应的
  if( driver.usable_time == 0 || time_before(jiffies, driver.usable_time ) )
    {
      refuse = true;
    }
  if( refuse )
    {
      printk( KERN_ALERT "GPS receiver is not setup fully\n" );
      return -EBUSY;
    }

  // 然后：看看是否设备已经被占用了，如果还没有那就可以继续
  down_interruptible( &driver.open_mutex );
  if( driver.is_open )
    {
      refuse = true;
    }
  if( refuse )
    {
      printk( KERN_ALERT "Duplicately open is not allowed!\n" );
      up( &driver.open_mutex );
      return -EMFILE;
    }

  // 再然后：发送数据告诉终端已经可以接收消息了
  line_coding *coding = kmalloc( sizeof(line_coding), GFP_KERNEL );
 get_rate:
  ret_code = get_line_coding( coding );
  if( ret_code < 0 )
    {
      printk( KERN_ALERT "get_line_coding exception:%d!\n", ret_code );
      kfree( coding );
      up( &driver.open_mutex );
      return -2;
    }
  else
    {
      if( coding->dwDTERate == COMM_SPEED ) // 不用再设置波特率了
	{
	  goto send_ready;
	}
    }

 set_rate:
  memset( coding, 0, sizeof(line_coding) );
  coding-> dwDTERate = COMM_SPEED;
  coding->bDataBits = 8;
  ret_code = set_line_coding( coding );
  if( ret_code < 0 )
    {
      printk( KERN_ALERT "set_line_coding exception:%d!\n", ret_code);
      kfree( coding );
      up( &driver.open_mutex );
      return -2;
    }
  else
    {
      ret_code = get_line_coding( coding );
      if( ret_code < 0 )
	{
	  printk( KERN_ALERT "get_line_coding exception after set_line_coding:%d!\n", ret_code );
	  kfree( coding );
	  up( &driver.open_mutex );
	  return -2;
	}
    }
 send_ready:
  ret_code = set_control_line_state( GPS_TTY_RDY );
  if( ret_code < 0 )
    {
      printk( KERN_ALERT "set_control_line_state exception:%d!\n", ret_code );
      kfree( coding );
      up( &driver.open_mutex );
      return -2;
    }
  else
    {
      // 由于每一次send_ready以后第一次读都会出现读到0字节的数据导致eof为真，所以
      // 干脆把第一次接收放在open这里
      usb_bulk_msg( driver.gps_usb_dev,
		    usb_rcvbulkpipe(driver.gps_usb_dev, driver.read_ep),
		    coding,
		    sizeof( line_coding ),
		    &ret_code,
		    OUT_OF_TIME );
      _printk( KERN_ALERT "open & read: %d bytes\n", ret_code );
      kfree( coding );
      driver.is_open = true;
      up( &driver.open_mutex );
      return 0;
    }
}

static int chrfile_close( struct inode *_inode, struct file *_file )
{
  if( !driver.is_open ){	/* 强制拨开 */
    return 0;
  }

  int ret_code = set_control_line_state( GPS_TTY_RTS );
  if( ret_code < 0 )
    {
      ret_code = set_control_line_state( GPS_TTY_RTS );
      if( ret_code < 0 )
	{
	  printk( KERN_ALERT "set_control_line_state exception:%d!\n", ret_code );
	  return -2;
	}
      else{
	goto success;
      }
    }
 success:
  down_interruptible( &driver.open_mutex );
  driver.is_open = false;
  up( &driver.open_mutex );
  return 0;
}

static int chrfile_read( struct file *_file, char __user *_des_buffer, size_t _buffer_len, loff_t *_offset )
{
  int read_len, ret_code, dest_len = (_buffer_len < RD_BUFFER_LEN)? _buffer_len:RD_BUFFER_LEN;

  // 首先：填写urb
  char *buffer = kmalloc( _buffer_len, GFP_KERNEL );

  // 然后：调用拿到数据
  down_interruptible( &driver.messaging );
  ret_code = usb_bulk_msg( driver.gps_usb_dev,
			   usb_rcvbulkpipe(driver.gps_usb_dev, driver.read_ep),
			   buffer,
			   dest_len,
			   &read_len,
			   OUT_OF_TIME ); 
  up(&driver.messaging );
  if( ret_code < 0 )
    {
      printk( KERN_ALERT "read from usb:%d\n", ret_code );
      kfree( buffer );
      return (ret_code == -ENODEV)? 0:ret_code; /* 是-ENODEV表示已经设备被拨出了 */
    }

  // 再返回
  ret_code = copy_to_user( _des_buffer, buffer, read_len );
  kfree( buffer );
  return read_len - ret_code;
}



static struct file_operations gps_chrfile_op = 
  {
    .owner = THIS_MODULE,
    .open = chrfile_open,
    .release = chrfile_close,
    .read = chrfile_read
  };

static int __init module_begin( void )
{
  const char *err_msg = NULL;
  int ret_code;

  /* 初始化全局变量 */
  init_MUTEX( &driver.open_mutex );
  init_MUTEX( &driver.messaging );

  /* 
     注册一个字符设备 
  */

  /* 动态分配一个cdev* */
  struct cdev* pdev = cdev_alloc();
  if( pdev == NULL )
    {
      ret_code = -ENOMEM;
      goto cdev_alloc_exception;
    }
  driver.gps_chrfile = pdev;

  /* 动态分配一个主设备号 */
  ret_code = alloc_chrdev_region( &driver.gps_id, 0, 1, "u-blox GPS receiver" );
  if( ret_code < 0 )
    {
      ret_code = -ENOMEM;
      goto alloc_chrdev_exception;
    }

  /* 请求成功，编定文件操作函数 */
  
  cdev_init( driver.gps_chrfile, &gps_chrfile_op );
  driver.gps_chrfile->owner = THIS_MODULE;
  ret_code = cdev_add( driver.gps_chrfile, driver.gps_id, 1 );
  if( ret_code < 0 )
    {
      goto cdev_add_exception;
    }
  _printk(KERN_ALERT "final code:%d:%d\n", MAJOR(driver.gps_id), MINOR(driver.gps_id) );

  /* 注册一个usb驱动 */
  ret_code = usb_register( &e581_driver );
  if( ret_code < 0 )
    {
      goto usb_register_exception;
    }
  return 0;

  /* 错误处理 */
 usb_register_exception:
  if( err_msg == NULL )
    err_msg = KERN_ALERT "error in usb_register\n";

  cdev_del( driver.gps_chrfile );
 cdev_add_exception:
  if( err_msg == NULL )
    err_msg = KERN_ALERT "error in cdev_add\n";

  unregister_chrdev_region( driver.gps_id, 1 );
 alloc_chrdev_exception:
  if( err_msg == NULL )
    err_msg = KERN_ALERT "error in alloc_chrdev_region\n";

  cdev_del(pdev);
 cdev_alloc_exception:
  if( err_msg == NULL )
    err_msg = KERN_ALERT "error in cdev_alloc\n";
  printk( err_msg );

  return ret_code;
}


//  ********************************************
//   以下是主要开始部分
//  ********************************************

static void __exit module_end( void )
{
  /* 清理 */

  /* 注消usb */
  usb_deregister( &e581_driver );

  /* 取消字符设备编定 */
  cdev_del( driver.gps_chrfile );

  /* 删除注册信息 */
  unregister_chrdev_region( driver.gps_id, 1 );
}

module_init( module_begin );
module_exit( module_end );


